<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>表格生成器</title>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        .tool-bar {
            display: flex;
            align-items: center;
            justify-content: flex-start;
        }

        .tool-item {
            margin: 0 10px;
        }

        .tool-item input {
            outline: none;
        }

        table {
            border-collapse: collapse;
            border-spacing: 0;
        }

        table>thead>tr>th {
            font-weight: bolder;
            background: #dedede;
        }

        td,
        th {
            padding: 8px;
        }

        table,
        td,
        th {
            border: solid 1px black;
        }

        td div,
        th div {
            outline: none;
        }

        .mask {
            position: fixed;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.1);
            justify-content: center;
            align-items: center;
            transition: all 1.5s;
            animation: fadeIn .1s;
        }

        .content {
            position: absolute;
            left: 0;
            top: 0;
            padding: 6px 4px;
            border-radius: 10px;
            background-color: #fff;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            color: #666;
            font-size: 14px;
            cursor: pointer;
            animation: fadeIn .3s;
        }

        .content .item {
            margin: 4px 0;
        }

        /*动画效果*/
        @-webkit-keyframes fadeIn {
            0% {
                opacity: 0;
                /*初始状态 透明度为0*/
            }

            50% {
                opacity: .5;
                /*中间状态 透明度为0.5*/
            }

            100% {
                opacity: 1;
                /*结尾状态 透明度为1*/
            }
        }
    </style>
</head>

<body>
    <div id="app">
        <div class="mask" v-show="isShowContextMenu">
            <div class="content" :style="contextMenuSty">
                <div class="item" @click="mergeCell()">合并单元格</div>
                <div class="item" @click="isShowContextMenu = false">关闭</div>
            </div>
        </div>
        <div class="tool-bar">
            <div class="tool-item"> <button id="addRow" @click="createTable">生成</button>
                行:<input id="rows" type="number" v-model="rows" maxlength="2">
                列:<input id="cols" type="number" v-model="cols" maxlength="2"></div>
            <div class="tool-item">
                <button id="addRow">添加一行</button>
            </div>
            <div class="tool-item">
                <button id="addCol">添加一列</button>
            </div>
            <div class="tool-item">
                <button id="delRow">删除一行</button>
            </div>
            <div class="tool-item">
                <button id="delCol">删除一列</button>
            </div>
        </div>
        <div class="table-wrap">
            <table>
                <thead v-show="!isClearTh">
                    <tr>
                        <th v-for="c in cols" :class="`c${c}`">
                            <div contenteditable='true'>
                                &nbsp;
                            </div>
                        </th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="(rows,ind) in tableData" :class="`line${ind}`" :key="`line${ind}`">
                        <td @mousedown="mousedown($event,vec.r,vec.c)" @mousemove="mousemove($event,vec.r,vec.c)"
                            @mouseup="mouseup($event,vec.r,vec.c)" v-for="vec in rows" :key="vec.id" :class="vec.id"
                            :style="vec.style" :colspan="vec.attr.colspan" :rowspan="vec.attr.rowspan">
                            <div contenteditable='true'>&nbsp;</div>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
    <textarea cols="60" rows="30" id="export"></textarea>

    <!-- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> -->
    <script>
        function RemoveSelection() {
            window.getSelection ? window.getSelection().removeAllRanges() : document.selection.createRange && (document.selection.createRange(), document.selection.empty())
        }
    </script>
    <script type="module">
        import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
        class Cell {
            constructor(r, c) {
                this.r = r
                this.c = c
            }
            static get zero() {
                return new Cell(0, 0)
            }
            equal(c2) {
                return this.r === c2.r && this.c === c2.c
            }
            static getRect(c1, c2) { // 计算两个点矩形包围盒内的每个坐标
                let x1 = Math.min(c1.r, c2.r), x2 = Math.max(c1.r, c2.r), y1 = Math.min(c1.c, c2.c), y2 = Math.max(c1.c, c2.c)
                let temp = []
                for (let i = x1; i <= x2; i++) {
                    let temp1 = []
                    for (let j = y1; j <= y2; j++) {
                        temp1.push(new Cell(i, j))
                    }
                    temp.push(temp1)
                }
                return temp
            }
        }


        let mouseDownCell = Cell.zero // 选中的单元格
        let lastMouseDownCell = Cell.zero // 选中的单元格
        let isMouseDown = false
        let isDraggedBetweenCells = false
        let rect = null
        let arr2Map = new Map()

        createApp({
            data() {
                return {
                    isShowContextMenu: false,
                    contextMenuSty: { left: 0, top: 0 },
                    rows: 5,
                    cols: 10,
                    isClearTh: false,

                    tableData: []
                }
            },
            methods: {
                createTable() {
                    let temp = []
                    for (let i = 0; i < this.rows; i++) {
                        temp.push([])
                        for (let j = 0; j < this.cols; j++) {
                            let cell = this.addCell(i, j)
                            arr2Map.set(cell.id, cell)
                            temp[i].push(cell)
                        }
                    }
                    this.tableData = temp
                },
                addCell(i, j, row = 1, col = 1) {
                    return { id: `r${i}_c${j}`, r: i, c: j, style: {}, attr: { colspan: col, rowspan: row } }
                },
                clearStyle(style = 'background') {
                    for (let i = 0; i < this.tableData.length; i++) {
                        for (let j = 0; j < this.tableData[i].length; j++) {
                            this.tableData[i][j].style[style] = ''
                        }
                    }
                },
                setCellStyle(Cell, style = 'background', val = '#d0eaf9') {
                    let id = `r${Cell.r}_c${Cell.c}`

                    if (!arr2Map.get(id)) return
                    arr2Map.get(id).style[style] = val
                },
                mousedown(event, row, col) {
                    if (1 === event.which) {
                        RemoveSelection();
                        isMouseDown = true;
                        this.clearStyle('background')
                        mouseDownCell = new Cell(row, col)
                    }
                },
                getIndexById(id) {
                    for (let i = 0; i < this.tableData.length; i++) {
                        for (let j = 0; j < this.tableData[i].length; j++) {
                            if (this.tableData[i][j].id === id) {
                                return { r: i, c: j }
                            }
                        }
                    }
                },
                mergeCell() {
                    if (!rect) return
                    let selectRow = rect.length
                    let selectCol = (rect[0] && rect[0].length) || 0

                    for (let line = 0; line < rect.length; line++) {

                        let col = rect[line][0]
                        let { r, c } = this.getIndexById(`r${col.r}_c${col.c}`)
                        console.log(r, c);
                        let startInd = c
                        let delNums = rect[line].length
                        if (line === 0) {
                            console.log(line, startInd, delNums);
                            this.tableData[r].splice(c, delNums, this.addCell(r, c, selectRow, selectCol))
                            continue
                        }
                        this.tableData[r].splice(startInd, delNums)

                    }
                    this.isShowContextMenu = false
                },
                mousemove(event, row, col) {
                    // 同一个格子内移动不重复计算，减少性能消耗
                    if (isMouseDown && !mouseDownCell.equal(new Cell(row, col)) && !lastMouseDownCell.equal(new Cell(row, col))) {
                        lastMouseDownCell = new Cell(row, col)

                        isDraggedBetweenCells = true
                        this.clearStyle('background')
                        rect = Cell.getRect(mouseDownCell, new Cell(row, col))
                        rect.forEach(row => {
                            row.forEach(cell => this.setCellStyle(cell, 'background', '#d0eaf9'))
                        })
                        // this.$forceUpdate()
                        // removeIndex()
                        // RemoveSelection();
                        // $(".s").removeClass("s");
                        // selectCells(mouseDownCell, this)
                    }

                },
                mouseup(event) {
                    if (isMouseDown) {
                        isMouseDown = false
                        mouseDownCell = Cell.zero
                        isDraggedBetweenCells = false
                    }
                }
            },
            created() {
                this.createTable()
                document.oncontextmenu = (e) => {
                    e.preventDefault();
                    let x = e.clientX
                    let y = e.clientY
                    this.contextMenuSty = { left: `${x}px`, top: `${y}px` }
                    this.isShowContextMenu = true
                };
            }
        }).mount('#app')
    </script>
</body>

</html>